-module(ggt).
-export([start/3]).
-import(werkzeug, [shuffle/1, logging/2, timeMilliSecond/0, to_String/1, reset_timer/3, get_config_value/2]).

start(PName, ArbZeit, TermZeit) ->
	register(PName, self()),

	Nachricht1 = lists:concat([PName, " Startzeit: ", werkzeug:timeMilliSecond(),"| mit PID ", to_String(self()), " auf ", node()]),
	log_message(Nachricht1, PName),

	%%kann man ändern, so dass es beim start des ggt übergeben wird
	{_,Handle} = file:consult("../config/ggt.cfg"),
	{_,NameserviceName} = get_config_value(nameservicename, Handle),
	{_,NameserviceNode} = get_config_value(nameservicenode, Handle),
	{_,KoordinatorName} = get_config_value(koordinatorname, Handle),

	net_adm:ping(NameserviceNode),%%globaler NS
	timer:sleep(1000),
	Nameservice = global:whereis_name(NameserviceName),%%KLC NS auf global NS suchen

	Nameservice ! {self(),{lookup,KoordinatorName}},
		receive 
        %%not_found -> io:format("..meindienst..not_found.\n"); 
        {pin,{Name,Node}} -> Koordinator = {Name,Node}
		end,

	Nameservice ! {self(),{rebind,PName,node()}},
		receive ok -> 
			log_message("beim Namensdienst und auf Node lokal registriert.", PName)
		end,

	Koordinator ! {hello, PName},
	log_message("beim Koordinator gemeldet.", PName),	
	main(Koordinator, PName, Nameservice,0 ,0 ,0 , ArbZeit, TermZeit, undefined, time(), 0, true).

main(Koordinator, PName, Nameservice, LeftN, RightN, Mi, ArbZeit, TermZeit, AbstimmungTimer, LastMiChange, AnzahlTerminierung, AbstimmungsFlag) ->
	
		receive 
			{setneighbors, LeftN1, RightN1} ->
				Nameservice ! {self(),{lookup,LeftN1}},
				receive
					{pin,{Name,Node}} -> LeftNPID = {Name,Node},
					Nachricht1 = lists:concat(["Linker Nachbar ", Name, " ", to_String(Node), " gebunden"]),
					log_message(Nachricht1, PName)
				end,
				Nameservice ! {self(),{lookup,RightN1}},
				receive
					{pin,{Name2,Node2}} -> RightNPID = {Name2,Node2},
					Nachricht2 = lists:concat(["Rechter Nachbar ", Name2, " ", to_String(Node2), " gebunden"]),
					log_message(Nachricht2, PName)
				end,
				main(Koordinator, PName, Nameservice, LeftNPID, RightNPID, Mi, ArbZeit, TermZeit, AbstimmungTimer, time(), AnzahlTerminierung, AbstimmungsFlag);

			{setpm, MiNeu} ->
				case AbstimmungTimer of
					undefined -> {ok, NewTimer} =  timer:send_after(TermZeit*1000, self(), startAbstimmung);
					_ -> NewTimer = werkzeug:reset_timer(AbstimmungTimer, TermZeit, startAbstimmung)
				end,
				log_message(lists:concat(["setpm: ", MiNeu]), PName),
				main(Koordinator, PName, Nameservice, LeftN, RightN, MiNeu, ArbZeit, TermZeit, NewTimer, time(), AnzahlTerminierung,true);

			{sendy, NewY} ->
				log_message(lists:concat(["Neues Y erhalten! ",NewY]), PName),
				case NewY < Mi of
					true ->
						MiNew = ((Mi - 1) rem NewY) + 1,

						Nachricht3 = lists:concat(["sendy: ", NewY, " ", Mi, "; berechnet als neues Mi", MiNew, ". ", werkzeug:timeMilliSecond()]),
						log_message(Nachricht3, PName),

						timer:sleep(ArbZeit*1000),
						LeftN ! {sendy, MiNew},
						RightN ! {sendy, MiNew},
						case (MiNew - Mi =/= 0) of
							true -> 
								Koordinator ! {briefmi, {PName, MiNew, werkzeug:timeMilliSecond()}}
						end;
					false ->
						MiNew = Mi,
						Nachricht5 = lists:concat(["sendy: ", NewY, " ", Mi, "; Keine Berechnung."]),
						log_message(Nachricht5, PName)
				end,
				ResettedTimer = werkzeug:reset_timer(AbstimmungTimer, TermZeit, startAbstimmung),
				main(Koordinator, PName, Nameservice, LeftN, RightN, MiNew, ArbZeit, TermZeit, ResettedTimer, time(), AnzahlTerminierung+1, true);

			{tellmi, PID} ->
				PID ! {mi, Mi},
				main(Koordinator, PName, Nameservice, LeftN, RightN, Mi, ArbZeit, TermZeit, AbstimmungTimer, LastMiChange, AnzahlTerminierung, AbstimmungsFlag);

			{pingGGT, PID} ->
				PID ! {pongGGT, PName},
				main(Koordinator, PName, Nameservice, LeftN, RightN, Mi, ArbZeit, TermZeit, AbstimmungTimer, LastMiChange, AnzahlTerminierung,AbstimmungsFlag);

			startAbstimmung when AbstimmungsFlag ->
				RightN ! {abstimmung, PName},
				Nachricht4 = lists:concat([PName, " initiiere eine Terminierungsabstimmung ", Mi, ". ", werkzeug:timeMilliSecond()]),
				log_message(Nachricht4, PName),
				main(Koordinator, PName, Nameservice, LeftN, RightN, Mi, ArbZeit, TermZeit, AbstimmungTimer, LastMiChange, AnzahlTerminierung, false);

			{abstimmung, OtherName} ->
				if 
					OtherName == PName ->
						Koordinator ! {briefterm, {PName, Mi, werkzeug:timeMilliSecond()}, self()},
						TerminierungNeu = AnzahlTerminierung+1,
						Nachricht6 = lists:concat([PName, ": stimme ab ", PName, ": Koordinator ", TerminierungNeu, "te Terminierung gemeldet mit ",
								Mi, werkzeug:timeMilliSecond()]),
						log_message(Nachricht6, PName);
					true ->
						TerminierungNeu = AnzahlTerminierung,
						calculateForwardingOfTermination(RightN, time(), LastMiChange, TermZeit, PName, OtherName)							
				end,
				main(Koordinator, PName, Nameservice, LeftN, RightN, Mi, ArbZeit, TermZeit, AbstimmungTimer, LastMiChange, TerminierungNeu,AbstimmungsFlag);

			kill ->
				kill(Nameservice, PName)
		end.

calculateForwardingOfTermination(RightN, Now, LastMiChange, Termzeit, PName, OtherName) ->
	{HourNow, MinNow, SekNow} = Now,
	{HourLast, MinLast, SekLast} = LastMiChange,

	NowInSek = (HourNow * 3600) + (MinNow * 60) + (SekNow),
	LastInSek = (HourLast * 3600) + (MinLast * 60) + (SekLast),

	Diff = NowInSek - LastInSek,

	case (Diff > Termzeit/2) of
		true ->
			RightN ! {abstimmung, OtherName},
			Nachricht1 = lists:concat([PName, ": stimme ab", OtherName, ": mit >JA< gestimmt und weitergel. (Code2). ", werkzeug:timeMilliSecond()]),
			log_message(Nachricht1, PName);
		false ->
			Nachricht2 = lists:concat([PName, ": stimme ab ", OtherName, ": mit >NEIN< geantwortet und ignoriert."]),
			log_message(Nachricht2, PName)
	end.

kill(Nameservice, PName) ->
	Nameservice ! {self(), {unbind, PName}},
	receive
		ok -> ok
			%% do something
	end,
	Nachricht1 = lists:concat(["Downtime: ", werkzeug:timeMilliSecond(), "vom Client ", PName]),
	log_message(Nachricht1, PName),
	unregister(PName).

log_message(Nachricht, PName) ->
	{_,Hostname} = inet:gethostname(),
	logging(lists:concat(["../logs/GGT ", PName, Hostname, ".log"]), lists:concat([Nachricht,"\n"])).